<?php
// @formatter:off
/**
 * @file WCMassiveInstaller.php
 * @author Alejandro Dario Simi
 * @date $Date: 2013-07-18 22:13:05 +0000 (Thu, 18 Jul 2013) $
 *
 * $Id: WCMassiveInstaller.php 97 2013-07-18 22:13:05Z daemonraco@gmail.com $
 * $URL: http://wcomix.googlecode.com/svn/branches/wcomix-1.0/includes/db/WCMassiveInstaller.php $
 */
// @formatter:on

//
// Including database items managers only when the system is not installed to
// avoid wasting time.
if(!$wcDefaults["installed"]) {
	require_once "{$wcPaths["includes-directory"]}/db/WCDBTable.php";
	require_once "{$wcPaths["includes-directory"]}/db/WCDBIndex.php";
}

/**
 * @class WCMassiveInstaller This class holds the logic to check the database
 * structure and take actions when it's as it should be.
 */
class WCMassiveInstaller {
	/**
	 * @var WCMassiveInstaller Pointer to the sole instance of this class.
	 */
	protected static $_Instance;
	/**
	 * @var WCConnection Shortcut for the database singleton.
	 */
	protected $_db;
	/**
	 * @var string Shortcut for the database prefix.
	 */
	protected $_dbprefix;
	/**
	 * Class constructor.
	 */
	protected function __construct() {
		//
		// Loading global pointers
		global $wcDatabaseAccess;
		//
		// Loading shortcuts.
		// @{
		$this->_db = WCConnection::Instance();
		$this->_dbprefix = $wcDatabaseAccess["prefix"];
		// @}
	}
	/**
	 * Prevent users to clone the instance.
	 */
	public function __clone() {
		trigger_error(__CLASS__.": Clone is not allowed.", E_USER_ERROR);
	}
	//
	// Public methods.
	public function install() {
		//
		// Loading global pointers.
		global $wcDefaults;
		//
		// Checking that Read Only Mode is not activated and the "already
		// installed" flag is not active.
		if(!$wcDefaults["read-only"] && !$wcDefaults["installed"]) {
			//
			// Loading global pointers.
			global $wcDatabase;
			//
			// A service must avoid any database alteration information.
			$silentDatabase = defined("__SERVICE__") || !$wcDefaults["debug"];
			//
			// Prompts on web pages require special tags like '<!--' and '-->'
			$onWeb = !defined("__SHELL__");
			//
			// Creating a temporary list for tables names.
			$tables = array();
			//
			// Creating a temporary list for all the queries needed
			// to make the database be as it should.
			$queries = array();
			//
			// Checking all required tables.
			foreach($wcDatabase["tables"] as $table) {
				//
				// Creating an object to manage current table
				// specifications.
				$item = new WCDBTable($table);
				//
				// Storing current table name.
				$tables[] = $item->name();
				//
				// Checking database table against
				// specifications.
				if(!$item->check()) {
					//
					// At this point, there's something wrong
					// with the table. Loading all required
					// DDL queries to correct the problem.
					foreach($item->ddlQueries() as $query) {
						$queries[] = $query;
					}
				}
				//
				// Freeing memory
				unset($item);
			}
			//
			// Checking all required indexes.
			foreach($wcDatabase["indexes"] as $index) {
				//
				// Creating an object to manage current index
				// specifications.
				$item = new WCDBIndex($index);
				//
				// Checking database index against
				// specifications.
				if(!$item->check()) {
					//
					// At this point, there's something wrong
					// with the index. Loading all required
					// DDL queries to correct the problem.
					foreach($item->ddlQueries() as $query) {
						$queries[] = $query;
					}
				}
				//
				// Freeing memory
				unset($item);
			}
			//
			// Checking if the current configuration requires an
			// aggressive treatment of database objects.
			if($wcDatabase["aggressive"]) {
				//
				// Loading all required DDL queries to remove
				// not unspecified database objects.
				foreach($this->removeUnknownTables($tables) as $query) {
					$queries[] = $query;
				}
			}
			//
			// Web debugging information opening tag.
			if(!$silentDatabase && $onWeb && isset($queries[0])) {
				echo "<!--\n";
			}
			//
			// Running all required queries to keep database updated.
			foreach($queries as $query) {
				//
				// Avoiding empty queries.
				if($query) {
					//
					// Prompting debug information.
					if(!$silentDatabase) {
						echo "Query: {$query}\n";
					}
					//
					// Executing DDL query.
					$this->_db->exec($query);
				}
			}
			//
			// Web debugging information opening tag.
			if(!$silentDatabase && $onWeb && isset($queries[0])) {
				echo "-->\n";
			}
		}
	}
	//
	// Protected methods.
	/**
	 * This method searches for unspecified tables based on a list of valid
	 * tables given by parameters.
	 *
	 * @param string[] $valids List of specified tables.
	 * @return string[] Returns a list of DDL queries required to eliminate
	 * unspecified tables.
	 */
	protected function removeUnknownTables(&$valids) {
		//
		// Creating a default value to be returned.
		$queries = array();
		//
		// Checking that there are valid tables to consider.
		if(count($valids) > 0) {
			//
			// Loading global pointers.
			global $wcDatabaseAccess;
			//
			// Creating a query to load all tables of the current
			// database schema not in the given list.
			// @{
			$query = "select  table_name\n";
			$query .= "from    information_schema.tables\n";
			$query .= "where   table_name not in ('".implode("','", $valids)."')\n";
			$query .= " and    table_schema = '{$wcDatabaseAccess["database"]}'\n";
			// @}
			//
			// Asking the list of tables from database.
			$result = $this->_db->query($query, true);
			//
			// Checking results.
			if($result && $result->rowCount() > 0) {
				//
				// Creating DDL queries to eliminate unwanted
				// tables.
				foreach($result->fetchAll() as $row) {
					$queries[] = "drop table {$row["table_name"]} cascade";
				}
			}
		}
		//
		// Returning the list of DDL queries.
		return $queries;
	}
	//
	// Public class methods.
	/**
	 * This method allows to access the sole instance of this class.
	 *
	 * @return WCMassiveInstaller Returns a singleton pointer.
	 */
	public static function Instance() {
		//
		// Checking if the first instance is not yet created.
		if(!isset(self::$_Instance)) {
			//
			// Creating singleton.
			$c = __CLASS__;
			self::$_Instance = new $c();
		}
		//
		// Returning the singleton.
		return self::$_Instance;
	}
}
?>